﻿using Nemerle.Collections;
using Nemerle.Extensions;
using Nemerle.Imperative;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Nemerle2.Compiler;
using Nemerle.Surround;

[assembly: DefineSurround("freeze_repaind_of_metadataTree", true, _metadataTree.BeginUpdate(), _metadataTree.EndUpdate())]

namespace DocBrowser
{
  /// <summary>Description of MetadataTree.</summary>
  [ToolboxBitmap(typeof(MetadataTree))]
  public partial class MetadataTree : UserControl
  {
    public this()
    {
      InitializeComponent();
      
      _metadataTree.BeforeExpand += 
        fun (_, arg)
        {
          InitNode(arg.Node)
        };
    }
    
    InitNode(node : TreeNode) : void
    {
      unless (node.Nodes.Count == 1 && node.Nodes[0].Tag == null)
        return;
            
      match (node.Tag :> Metadata)
      {
        | Namespace(_, typeInfos, macroInfos) => LoadTypesContent(node, typeInfos, macroInfos);
        | Assembly(assembly)                  => LoadAssemblyContent(node, assembly);
        | TypeInfo(type)                      => _ = type; ()
        | MacroInfo(m)                        => _ = m; ()
      }
    }
    
    [System.ComponentModel.ReadOnly(true)]
    public LoadedAssemblyPaths  : list[string]
    {
      get { _metadataTree.Nodes.OfType.[TreeNode]().MapToList(n => (n.Tag :> Metadata.Assembly).Value.ImagePath ) }
    }
    
    [System.ComponentModel.ReadOnly(true)]
    public CurrentSelectionPath : list[string]
    {
      get
      {
        def makePath(node : TreeNode, acc : list[string] = [])
        {
          if (node.Parent == null)
            (node.Text :: acc)
          else
            makePath(node.Parent, node.Text :: acc)
        }
        
        if (_metadataTree.SelectedNode == null)
          []
        else
          makePath(_metadataTree.SelectedNode)
      }
      set  
      {
        //_metadataTree.Nodes
        def openPath(nodes : TreeNodeCollection, path : list[string], node : option[TreeNode]) : void
        {
          def nodesAry = nodes.Cast.[TreeNode]().ToArray();
          match (path)
          {
            | head :: tail => 
              match (nodesAry.Find(n => n.Text == head))
              {
                | Some(node) =>
                  InitNode(node);
                  //_metadataTree.SelectedNode = node;
                  //node.Expand();
                  //_ = node.IsExpanded;
                  openPath(node.Nodes, tail, Some(node))
                | _ =>
                  when (node is Some(n))
                    _metadataTree.SelectedNode = n;
              }
            | [] => 
                  when (node is Some(n))
                    _metadataTree.SelectedNode = n;
          }
        }
        
        openPath(_metadataTree.Nodes, value, None())
      }
    }


    public SelectedMetadata : Metadata { get; private set; }
    public event SelectedMetadataChanged : Action[Metadata];
    internal Host : Host { get; set; }
    
    public OpenAssemblyFromFile(path : string) : void
    {
      def assembly = Host.LoadAssembly(path);
      
      when (assembly != null)
      {
        def node = TreeNode(assembly.Name);
        node.Tag = Metadata.Assembly(assembly);
        HasNested(node);
        _ = _metadataTree.Nodes.Add(node);
      }
    }
    
    LoadTypesContent(parentNode : TreeNode, types : ReadOnlyArray[ITypeInfo], macros : ReadOnlyArray[IMacroInfo]) : void
    {
      surroundwith(freeze_repaind_of_metadataTree)
      {
        parentNode.Nodes.Clear();

        def nodes = macros.OrderBy(_.Name).MapToArray(
          t => { def n = TreeNode($"$(t.Name) [Macro]"); n.Tag = Metadata.MacroInfo(t); n });
        
        parentNode.Nodes.AddRange(nodes);
        
        def nodes = types.OrderBy(_.Name).MapToArray(
          t => { def n = TreeNode(t.Name); n.Tag = Metadata.TypeInfo(t); HasNested(n); n });
        
        parentNode.Nodes.AddRange(nodes);
      }
    }
    
    LoadAssemblyContent(parentNode : TreeNode, assembly : IReferencedAssembly) : void
    {
      surroundwith(freeze_repaind_of_metadataTree)
      {
        def nodes = assembly.Namespaces.Where((_, types, macros) => !(types.IsEmpty() && macros.IsEmpty()))
                                       .MapToArray(
          fun ((nsName, types, macros))
          {
            def n = TreeNode(nsName);
            n.Tag = Metadata.Namespace(nsName, types, macros);
            HasNested(n);
            n
          });
        //def nodes      = namespaces.MapToArray(ns => TreeNode(ns.Key) <- { Tag = ns.ToArray(); });
      
        parentNode.Nodes.Clear();
        parentNode.Nodes.AddRange(nodes);
      }
    }
    
    HasNested(node : TreeNode) : void
    {
      _ = node.Nodes.Add(""); // add fake node
    }
    
    OnSelectedMetadataChanged(metadata : Metadata) : void
    {
      when (SelectedMetadataChanged != null)
        SelectedMetadataChanged(metadata);
    }
  
    _metadataTree_AfterSelect (_ : object,  e : System.Windows.Forms.TreeViewEventArgs) : void
    {
      def metadata = e.Node.Tag :> Metadata;
      
      when (metadata != null)
      {
        SelectedMetadata = metadata;
        OnSelectedMetadataChanged(metadata);
      }
    }
  }
}
